﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using System.Runtime.Serialization;
using System.IO;
using System.Diagnostics;
using NMigrate.Tests.Data;
using System.Data.SqlClient;
using System.Web.Security;

namespace NMigrate.Tests
{
	public class SpyHandler : FileHandler
	{
		#region FileHandler Members

		public override string Extension
		{
			get
			{
				return "toto";
			}
		}

		[DebuggerHidden]
		public void AssertWasCalled(params int[] numbers)
		{
			var allPresent = numbers.All(n => _Numbers.Contains(n));
			Assert.True(allPresent, "some script number should have executed");
		}
		[DebuggerHidden]
		public void AssertWasNotCalled(params int[] numbers)
		{
			var present = numbers.Any(n => _Numbers.Contains(n));
			Assert.False(present, "some script number should not have executed");
		}
		List<int> _Numbers = new List<int>();
		public override void Execute(FileInfo file, Configuration args)
		{
			if(_ExceptionFiles.Contains(file.Name))
				throw new InvalidOperationException("Spy exception test");
			var fileName = FileName.Parse(file);
			_Numbers.Add(fileName.Number);
		}

		public override bool IsTemplated(FileInfo file)
		{
			return false;
		}

		#endregion

		List<string> _ExceptionFiles = new List<string>();
		public void ThrowExceptionFor(string fileName)
		{
			_ExceptionFiles.Add(fileName);
		}
	}

	[DataContract]
	public class Environment
	{
		[DataContract]
		public class DatabaseEnvironment
		{
			[DataMember]
			public String FullConnectionString
			{
				get;
				set;
			}
			public string ExceptionProperty
			{
				get
				{
					var builder = new SqlConnectionStringBuilder(FullConnectionString);
					builder.InitialCatalog = null;
					return builder.ToString();
				}
			}
			public string DatabaseName
			{
				get
				{
					return new SqlConnectionStringBuilder(FullConnectionString).InitialCatalog;
				}
			}
		}

		[DataMember]
		public DatabaseEnvironment Database
		{
			get;
			set;
		}
	}
	public class UnitTests
	{
		[DataContract]
		public class SubTestConfig
		{
			[DataMember]
			public string Blabla
			{
				get;
				set;
			}
			[DataMember]
			public string Blabla2
			{
				get;
				set;
			}
		}
		[DataContract]
		public class TestConfig
		{
			[DataMember]
			public String Toto
			{
				get;
				set;
			}
			[DataMember]
			public String Tata
			{
				get;
				set;
			}
			[DataMember]
			public String Titi
			{
				get;
				set;
			}

			[DataMember]
			public SubTestConfig SubTest
			{
				get;
				set;
			}
		}

		[DataContract]
		public class TestTemplatedConfig
		{
			[DataMember]
			public String DirectoryName
			{
				get;
				set;
			}
		}

		[Fact]
		public void CanGetConfigFileConfiguration()
		{
			var configuration = new JsonConfiguration<TestConfig>("Data/TestConfig1.json");
			var config = configuration.GetValue();
			Assert.NotNull(config);
			Assert.Equal("hello", config.Tata);
			Assert.Equal("world", config.Toto);
			Assert.Equal(null, config.Titi);
			Assert.Equal("blabla one", config.SubTest.Blabla);
		}


		[Fact]
		public void CanReadVSMadeFile() //Bug vs with encoding :(
		{
			var configuration = new JsonConfiguration<Environment>("Data/Environment.conf");
			var config = configuration.GetValue();
			Assert.NotNull(config);
			Assert.Equal(@"Data Source=.\SQLEXPRESS;Initial Catalog=Easydhome;Integrated Security=True;", config.Database.FullConnectionString);
		}


		[Fact]
		public void StopMigrationWhenExceptionDuringTemplating()
		{
			Migration migration = new Migration();
			var spy = migration.FileHandlers.Add(new SpyHandler());
			migration.FileHandlers.Find<ConfigFileHandler>().DefaultAppExePath = "Toto.exe";
			var result = migration.Migrate("Data/MigrationWithTemplateError");
			Assert.Equal(2, result.CurrentVersion);
			spy.AssertWasNotCalled(4);
		}

		[Fact]
		public void CanExecuteBatchFile()
		{
			if(Directory.Exists("Hello"))
				Directory.Delete("Hello");
			BatchFileHandler batchFileHandler = new BatchFileHandler();
			batchFileHandler.Execute(new FileInfo("Data/Test.bat"), null);
			Assert.True(Directory.Exists("Hello"), "directory Hello should exist");
		}

		[Fact]
		public void CanExecuteBatchFileTemplated()
		{
			if(Directory.Exists("HelloTemplate"))
				Directory.Delete("HelloTemplate");
			BatchFileHandler batchFileHandler = new BatchFileHandler();
			Migration migration = new Migration();
			migration.Configuration = new JsonConfiguration<TestTemplatedConfig>("Data/TestTemplatedConfig.json");
			migration.MigrateFile("Data/TestTemplated.bat");
			Assert.True(Directory.Exists("HelloTemplate"), "directory HelloTemplate should exist");
		}

		[Fact]
		public void CanExecuteMigrationDirectory()
		{
			Migration migration = new Migration();
			var spy = migration.FileHandlers.Add(new SpyHandler());
			var result = migration.Migrate("Data/Migration").ToTester();
			spy.AssertWasCalled(1, 2, 3);
			result.AssertWereCalled("1.First.toto")
					.Then("2.Second.toto")
					.Then("3.Third.toto");
			Assert.Equal(3, result.Result.CurrentVersion);
		}
		[Fact]
		public void CanExecuteMigrationDirectoryFromVersionNumber()
		{
			Migration migration = new Migration();
			var spy = migration.FileHandlers.Add(new SpyHandler());
			var result = migration.Migrate("Data/Migration", 2).ToTester();
			spy.AssertWasCalled(3);
			spy.AssertWasNotCalled(1, 2);
			result.AssertWereCalled("3.Third.toto");
		}

		[Fact]
		public void ThrowIfError()
		{
			Migration migration = new Migration();
			var spy = migration.FileHandlers.Add(new SpyHandler());
			spy.ThrowExceptionFor("2.Second.toto");
			try
			{
				migration.Migrate("Data/Migration").ThrowIfError();
				Assert.False(true, "Should have thrown");
			}
			catch(MigrationException ex)
			{
				Assert.NotNull(ex.VersionMigration);
				Assert.NotNull(ex.FileMigration);
				Assert.NotNull(ex.InnerException);
			}
		}

		[Fact]
		public void DontThrowIfNoError()
		{
			Migration migration = new Migration();
			var spy = migration.FileHandlers.Add(new SpyHandler());
			try
			{
				migration.Migrate("Data/Migration").ThrowIfError();
			}
			catch(MigrationException)
			{
				Assert.False(true, "should have not throw migration exception");
			}
		}

		[Fact]
		public void CanExecuteMigrationScriptInParallel()
		{
			Migration migration = new Migration();
			var spy = migration.FileHandlers.Add(new SpyHandler());
			var result = migration.Migrate("Data/ParallelMigration").ToTester();
			result.AssertWereCalled("1.First.toto")
					.Then("2.Second.toto", "2.Second(2).toto")
					.Then("3.Third.toto");
		}

		[Fact]
		public void StopWhenError()
		{
			Migration migration = new Migration();
			var spy = migration.FileHandlers.Add(new SpyHandler());
			spy.ThrowExceptionFor("2.Second(2).toto");
			var result = migration.Migrate("Data/ParallelMigration").ToTester();
			result.AssertWereCalled("1.First.toto")
					.Then("2.Second.toto", "2.Second(2).toto")
					.ThenNothing();
		}

		

		[Fact]
		public void CanExecuteCSFile()
		{
			if(File.Exists(CSharpScript.FileName))
				File.Delete(CSharpScript.FileName);
			Migration migration = new Migration();
			migration.MigrateFile("Data/CSharpScript.cs");
			Assert.True(File.Exists(CSharpScript.FileName), "file " + CSharpScript.FileName + " should exist");
		}

		[Fact]
		public void CanCreateSqlMembershipProviderWithFactory()
		{
			var factory = new SqlMembershipProviderFactory();
			factory.ConnectionString = "myconnectionString";
			factory.EnablePasswordReset = true;
			factory.EnablePasswordRetrieval = false;
			factory.RequiresQuestionAndAnswer = false;
			factory.PasswordFormat = MembershipPasswordFormat.Clear;
			var membership = factory.CreateMembershipProvider();
			Assert.Equal(factory.EnablePasswordRetrieval, membership.EnablePasswordRetrieval);
			Assert.Equal(factory.EnablePasswordReset, membership.EnablePasswordReset);
			Assert.Equal(factory.RequiresQuestionAndAnswer, membership.RequiresQuestionAndAnswer);
			Assert.Equal(factory.PasswordFormat.Value, membership.PasswordFormat);
		}
	}
}
